/*
* Copyright © Shanghai Awinic Technology Co., Ltd. 2020-2020. All rights reserved.
* Description: i2c_software driver related library function files.
* Date： 20201126
* Awinic_Version： aw_soc_driver_v1.0.0
*/
#include "compile_option.h"
#ifdef AW_86802

#include "aw8680x_global.h"

/**
  * @brief  This function is used to send a start signal.
  * @param  None
  * @retval None
  */
static void aw_i2c_start(void)
{
	I2C_SCL_1();
	I2C_DELAY();
	I2C_SDA_1();
	I2C_SDA_0();
	I2C_DELAY();
	I2C_SCL_0();
	I2C_DELAY();
}

/**
  * @brief  This function is used to send a stop signal.
  * @param  None
  * @retval None
  */
static void aw_i2c_stop(void)
{
	I2C_SDA_0();
	I2C_DELAY();
	I2C_SCL_1();
	I2C_DELAY();
	I2C_SDA_1();
}

/**
  * @brief  This function is used to send a byte to SDA.
  * @param  AW_U8 byte
  * @retval None
  */
static void aw_i2c_send_byte(AW_U8 byte)
{
	AW_U32 i = 0;

	for (i = 0; i < I2C_DATABITS_MAX; i++) {
		if (byte & DATA_BIT7_MASK) {
			I2C_SDA_1();
		} else {
			I2C_SDA_0();
		}
		I2C_DELAY1();
		I2C_SCL_1();
		byte <<= DATA_BIT1_MASK;
		I2C_DELAY();
		I2C_DELAY1();
		I2C_SCL_0();
		I2C_DELAY();
		I2C_DELAY();
	}
}

/**
  * @brief  This function is used to wait for a ack from slave.
  * @param  None
  * @retval None
  */
static AW_U8 aw_i2c_wait_ack(void)
{
	AW_U8 ack = 0;

	I2C_SDA_1();
	I2C_DELAY();
	I2C_SCL_1();
	I2C_DELAY();
	ack = SDA_GET_LEVEL();
	I2C_SCL_0();
	I2C_DELAY();
	I2C_SDA_1(); // Release SDA and set high level for SDA to read data.
	if (ack == I2C_NACK) {
		aw_i2c_stop(); // Send a stop signal.
		return I2C_ERROR;
	}

	return I2C_SUCCESS;
}

/**
  * @brief  This function is used to put a ack to slave.
  * @param  None
  * @retval None
  */
static void aw_put_ack(void)
{
	I2C_SDA_0();
	I2C_DELAY1();
	I2C_SCL_1();
	I2C_DELAY1();
	I2C_DELAY1();
	I2C_SCL_0();
	I2C_DELAY();
	I2C_SDA_1();
	I2C_DELAY();
}

/**
  * @brief  This function is used to put a nack to slave.
  * @param  None
  * @retval None
  */
static void aw_put_nack(void)
{
	I2C_DELAY1();
	I2C_SDA_1();
	I2C_SCL_1();
	I2C_DELAY1();
	I2C_SCL_0();
	I2C_DELAY1();
	I2C_SDA_0();
	I2C_DELAY1();
}

/**
  * @brief  This function is that master read a byte from SDA.
  * @param  None
  * @retval None
  */
static AW_U8 aw_i2c_read_byte(void)
{
	AW_U8 read_value = 0;
	AW_U32 i = 0;
	for (i = 0; i < I2C_DATABITS_MAX; i++) {
		read_value <<= DATA_BIT1_MASK;
		I2C_SCL_1();
		read_value |= SDA_GET_LEVEL(); // Read SDA level.
		I2C_SCL_0();
		I2C_DELAY2();
	}

	return read_value;
}

/**
  * @brief  This function is that master read bytes from slave.
  * @param  p_data points to the first address where the data is stored
  * @param  size is the length of the read data
  * @retval None
  */
I2C_SOFTWARE_STATUS_E i2c_software_read_bytes(AW_U8 *p_data, AW_U16 size)
{
	AW_U32 i = 0;
	AW_S32 i2c_soft_timeout = I2C_SOFT_TIMEOUT;

	if (p_data == AW_NULL) {
		return I2C_ERROR;
	}

	/* In the open-drain mode, first set the level high,
		and set the GPIO port to a high impedance state. */
	GPIO_SET_HIGH(I2C_SCL_GPIO_PIN);
	GPIO_SET_HIGH(I2C_SDA_GPIO_PIN);

	aw_i2c_start(); // Send I2C start signal again.
	aw_i2c_send_byte((SLAVE_ADDR << DATA_BIT1_MASK) | I2C_RD); // Send slave address and read control.

	/* Wait for slave ack. */
	if (aw_i2c_wait_ack() == I2C_ERROR) {
		return I2C_ERROR;
	}

	I2C_SCL_1(); // Pulling up CLK is used to read the CLK level of the slave.

	while (SCL_GET_LEVEL() == 0) {
		if((i2c_soft_timeout--) <= 0) {
			return I2C_ERROR;
		}
	}

	/* Read data from slave. */
	for (i = 0; i < size; i++) {
		*p_data = aw_i2c_read_byte();
		p_data++;
		if (i != size - DATA_BIT1_MASK) {
			aw_put_ack(); // If it is not data befor last data, put a ack.
		} else {
			aw_put_nack(); // If it is data befor last data, put a nack.
		}
	}

	aw_i2c_stop(); // Send I2C stop signal.
	return I2C_SUCCESS;
}

/**
  * @brief  This function is that master writes bytes to slave.
  * @param  p_data points to the first address where the data is stored
  * @param  size is the length of the read data
  * @retval I2C_SOFTWARE_STATUS_E status
  */
I2C_SOFTWARE_STATUS_E i2c_software_write_bytes(AW_U8 *p_data, AW_S16 size)
{
	if ((p_data == AW_NULL) || (size <= 0)) {
		return I2C_ERROR;
	}

	/* In the open-drain mode, first set the level high,
		and set the GPIO port to a high impedance state. */
	GPIO_SET_HIGH(I2C_SCL_GPIO_PIN);
	GPIO_SET_HIGH(I2C_SDA_GPIO_PIN);

	aw_i2c_start(); // Send I2C start signal.
	aw_i2c_send_byte((SLAVE_ADDR << DATA_BIT1_MASK) | I2C_WR); // Send slave address and write control.

	/* Wait for slave ack. */
	if (aw_i2c_wait_ack() == I2C_ERROR) {
		return I2C_ERROR;
	}

	/* Write data to slave. */
	while (size--) {
		aw_i2c_send_byte(*p_data); // Write data to slave register.
		p_data++;

		/* Wait for slave ack. */
		if (aw_i2c_wait_ack() == I2C_ERROR) {
			return I2C_ERROR;
		}
	}

	aw_i2c_stop(); // Send I2C stop signal.
	return I2C_SUCCESS;
}
#endif
